﻿// MyProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//公众号：程序员速成 ，内含一辈子都让你感激自己的优质视频教程，欢迎关注

#include <iostream>
#include "Fighter.h"
#include "ItemStrategy.h"

#ifdef _DEBUG   //只在Debug（调试）模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{	
	class M_Undead //亡灵类怪物
	{
	public:
		void getinfo()
		{
			cout << "这是一只亡灵类怪物" << endl;
		}
		//......其他代码略
	};

	class M_Element //元素类怪物
	{
	public:
		void getinfo()
		{
			cout << "这是一只元素类怪物" << endl;
		}
		//......其他代码略
	};

	class M_Mechanic //机械类怪物
	{
	public:
		void getinfo()
		{
			cout << "这是一只机械类怪物" << endl;
		}
		//......其他代码略
	};

	//战士主角
	class F_Warrior
	{
	public:
		void attack_enemy_undead(M_Undead* pobj) //攻击亡灵类怪物
		{
			//进行攻击处理......
			pobj->getinfo(); //可以调用亡灵类怪物相关的成员函数
		}

	public:
		void attack_enemy_element(M_Element* pobj) //攻击元素类怪物
		{
			//进行攻击处理......
			pobj->getinfo(); //可以调用元素类怪物相关的成员函数
		}

		//其他代码略......
	};
}
namespace _nmsp2
{
	class Monster //作为所有怪物类的父类（抽象层）
	{
	public:
		virtual void getinfo() = 0; //纯虚函数
		virtual ~Monster() {} //做父类时析构函数应该为虚函数
	};
	class M_Undead :public Monster//亡灵类怪物
	{
	public:
		virtual void getinfo()
		{
			cout << "这是一只亡灵类怪物" << endl;
		}
		//......其他代码略
	};

	class M_Element :public Monster//元素类怪物
	{
	public:
		virtual void getinfo()
		{
			cout << "这是一只元素类怪物" << endl;
		}
		//......其他代码略
	};

	class M_Mechanic :public Monster//机械类怪物
	{
	public:
		virtual void getinfo()
		{
			cout << "这是一只机械类怪物" << endl;
		}
		//......其他代码略
	};

	//战士主角
	class F_Warrior
	{
	public:
		void attack_enemy(Monster* pobj) //攻击怪物
		{
			//进行攻击处理......
			pobj->getinfo(); //可以调用怪物相关的成员函数
		}
		//其他代码略......
	};
}

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
		
	//第4章 策略（Strategy）模式
	//（1）一个具体实现范例的逐步重构
	//补血道具（药品）：
	  //a：补血丹：补充200点生命值
	  //b：大还丹：补充300点生命值
	  //c：守护丹：补充500点生命值
	//Fighter，F_Warrior，F_Mage
	//策略  设计模式的定义：定义一系列算法（策略类），将每个算法封装起来，让它们可以相互替换。换句话说，策略模式通常把一系列算法
	   // 封装到一系列具体策略类中来作为抽象策略类的子类，然后根据实际需要使用这些子类。
	//策略类中的三种角色
	//a)Context（环境类）：该类中维持着一个对抽象策略类的指针或引用。这里指Fighter类。
	//b)Stategy（抽象策略类）：定义所支持的算法的公共接口，是所有策略类的父类。这里指ItemStrategy类。
	//c)ConcreteStrategy（具体策略类）：抽象策略类的子类，实现抽象策略类中声明的接口。这里指ItemStrategy_BXD、ItemStrategy_DHD、ItemStrategy_SHD。
	//策略类的优点：
	//a)以扩展的方式支持对未来的变化，符合开闭原则。
	  //遇到大量不稳定的if条件分支 或者switch分支，就要优先考虑是否可以通过策略模式来解决。策略模式是if，switch条件分支的杀手。
	//b)算法可以被复用。
	//c)策略模式可以看成是类继承的一种替代方案。通过为环境类对象指定不同的策略，就可以改变环境类对象的行为。
	//策略类的缺点：
	//a)导致引入许多新策略类；
	//b)使用策略时，调用者（main主函数）必须熟知所有策略类的功能并根据实际需要自行决定使用哪个策略类。

	//（2）依赖倒置原则：Dependency Inversion Principle，简称DIP
	 //是面向独享设计的主要实现方法，同时 也是实现开闭原则的重要实现途径。
	//解释：高层组件不应该依赖于低层（具体实现类），两者都应该依赖于抽象层。
	//范例：工厂模式时，亡灵类M_Undead，元素类M_Element，机械类M_Mechanic。








	/*
	Fighter* prole_war = new F_Warrior(1000, 0, 200); //这里没有采用工厂模式，如果主角很多，可以考虑采用工厂模式创建对象
	prole_war->UseItem(LF_DHD);

	delete prole_war;	 
	*/

	/*
	//创建主角
	Fighter* prole_war = new F_Warrior(1000, 0, 200);

	//吃一颗大还丹
	ItemStrategy* strategy = new ItemStrategy_DHD(); //创建大还丹策略
	prole_war->SetItemStrategy(strategy); //主角设置大还丹策略，准备吃大还丹
	prole_war->UseItem(); //主角吃大还丹

	//再吃一颗补血丹
	ItemStrategy* strategy2 = new ItemStrategy_BXD(); //创建补血丹策略
	prole_war->SetItemStrategy(strategy2);//主角设置补血丹策略，准备吃补血丹
	prole_war->UseItem(); //主角吃补血丹

	delete strategy;
	delete strategy2;
	delete prole_war;
	*/

	/*
	_nmsp1::M_Undead* pobjud = new _nmsp1::M_Undead();
	_nmsp1::F_Warrior* pobjwar = new _nmsp1::F_Warrior();
	pobjwar->attack_enemy_undead(pobjud); //攻击一只亡灵类怪物

	_nmsp1::M_Element* pobjelm = new _nmsp1::M_Element();
	pobjwar->attack_enemy_element(pobjelm); //攻击一只元素类怪物

	//资源释放
	delete pobjwar;
	delete pobjud;
	delete pobjelm;*/

	_nmsp2::Monster* pobjud = new _nmsp2::M_Undead();
	_nmsp2::F_Warrior* pobjwar = new _nmsp2::F_Warrior();
	pobjwar->attack_enemy(pobjud); //攻击一只亡灵类怪物

	_nmsp2::Monster* pobjelm = new _nmsp2::M_Element();
	pobjwar->attack_enemy(pobjelm); //攻击一只元素类怪物

	//资源释放
	delete pobjwar;
	delete pobjud;
	delete pobjelm;
	
	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件

